home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / sound / soundlevel / soundlevel.c next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  27.8 KB  |  1,207 lines

  1. /*
  2.     File:        SoundLevel.c
  3.  
  4.     Contains:    A simple application that echos the sound input level on the microphone using
  5.                 a display much like a VCR peak-hold bar meter.
  6.     
  7.                 Its a fun app, to use up those wasted background cycles and seems to amuse
  8.                 people to no end. At 40k, its not a major memory hog so pop it in your Startup
  9.                 Items folder and enjoy.
  10.     
  11.                 The About Box introduces a new power user short cut. Clap hard enough to raise
  12.                 the sound threshold about ≈ 95% and the dialog will dismiss. No need to reach
  13.                 and strain for that enter key anymore!
  14.     
  15.                 Caveats:
  16.                     • Not much error checking.
  17.                     • Too many globals.
  18.                     • Saves the window position back to the app resource fork.
  19.                     • Requires a Sound Input Device (MacRecorder will do just fine)
  20.                     • It keeps the sound input device open with r/w so you won’t be able
  21.                           to use other sound input apps unless you quit.
  22.     
  23.                 Have fun and let me know what you think.
  24.  
  25.     Written by: Ken Bereskin    
  26.  
  27.     Copyright:    Copyright © 1992-1999 by Apple Computer, Inc., All Rights Reserved.
  28.  
  29.                 You may incorporate this Apple sample source code into your program(s) without
  30.                 restriction. This Apple sample source code has been provided "AS IS" and the
  31.                 responsibility for its operation is yours. You are not permitted to redistribute
  32.                 this Apple sample source code as "Apple sample source code" after having made
  33.                 changes. If you're going to re-distribute the source, we require that you make
  34.                 it clear in the source that the code was descended from Apple sample source
  35.                 code, but that you've made changes.
  36.  
  37.     Change History (most recent first):
  38.                 8/2/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  39.                 
  40.  
  41. */
  42.  
  43. /*    ---------------------------------------------------------------------------------
  44.     ToolBox header files
  45. */
  46. #include <Types.h>
  47. #include <QuickDraw.h>
  48. #include <Resources.h>
  49. #include <Windows.h>
  50. #include <Fonts.h>
  51. #include <Events.h>
  52. #include <TextEdit.h>
  53. #include <Dialogs.h>
  54. #include <Menus.h>
  55. #include <Memory.h>
  56. #include <Desk.h>
  57. #include <ToolUtils.h>
  58. #include <OSUtils.h>
  59. #include <Errors.h>
  60. #include <Folders.h>
  61. #include <OSEvents.h>
  62. #include <SegLoad.h>
  63. #include <Traps.h>
  64. #include <SoundInput.h>
  65. #include <GestaltEqu.h>
  66.  
  67.  
  68. /*    ---------------------------------------------------------------------------------
  69.     Forward reference prototypes
  70. */
  71. void    Initialize(void);
  72. void    SetupEnvironment(void);
  73. void    SetupMemory(void);
  74. void    SetupMenus(void);
  75. void    DoEvents(void);
  76. void    DoMouseDown (EventRecord *event);
  77. void    DoKeyDown (EventRecord *event);
  78. void    DoUpdateWindow (WindowPtr window);
  79. void    DoActivateWindow (Boolean activate, WindowPtr window);
  80. void    DoMenuCommand(short theMenu, short theItem);
  81. void    DoContentClick(EventRecord *event, WindowPtr window);
  82. void    DoSuspend(EventRecord *event);
  83. void    DoResume(EventRecord *event);
  84. void    DoIdle(void);
  85. void    DoDeskAccessory(short whichMenu, short whichItem);
  86. void    NewLevelWindow(void);
  87. void    CleanUp(void);
  88. void    DoAbout(void);
  89. Boolean    OptionTest(void);
  90. Boolean CmdTest(void);
  91. Boolean ShiftTest(void);
  92. Boolean    ColourQDExists (void);
  93. Boolean ColourQDIsOn (void);
  94. short    ColourDepth (void);
  95. void     TwitchToFinder(void);
  96. void    DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour);
  97. void     GetBarRect(Rect *meterRect, short whichBar, Rect *barRect);
  98. void     InitLevelMeter (void);
  99. void    CloseLevelMeter (void);
  100. OSErr     OpenTheSoundDevice(void);
  101. void     DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour);
  102. pascal  Boolean AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item);
  103. void     HiliteItem(DialogPtr dialog, short theitem);
  104. void    UpdateAboutDialog(WindowPtr window);
  105. void    MakeCursor(short cursorID);
  106.  
  107. /*    ---------------------------------------------------------------------------------
  108.     Constants
  109. */
  110. #define kMaxVolume                255
  111. #define kPeakTimeoutTicks        60            // number of ticks needed to timeout peak
  112.  
  113. #define kMenuBarID    128
  114.  
  115. #define kAppleMenu        128
  116. #define kAboutItem        1
  117.  
  118. #define kFileMenu        129
  119. #define kQuitItem        1
  120.  
  121. #define kGestaltLevelMeter    'SnLv'
  122.  
  123. /*    ---------------------------------------------------------------------------------
  124.     Global variables
  125. */
  126. Boolean        WNEIsImplemented;                // is WaitNextEvent avaiable
  127. Boolean     gQuitFlag = false;                // set to force a quit
  128. WindowPtr     gLevelWindow;                    // sound level window pointer
  129. RGBColor    blueRGB, redRGB, blackRGB;        // rgb colours used for drawing meter
  130. short        gNumElements;                    // number of elements in the meter
  131. long        gPeakTimeOut;
  132. short        gPeakLevel;
  133. short         gLastLevel;
  134. short        gRedZone;
  135. Rect        gMeterRect;
  136. long        gSoundInputRefNum;
  137. Boolean        gUseColour;
  138.  
  139. #ifdef powerc
  140.    QDGlobals    qd;
  141. #endif
  142.  
  143. /*    ---------------------------------------------------------------------------------
  144.     Main
  145.     
  146.     Application main entry point
  147. */
  148. void main()
  149. {
  150.     Initialize();                    // do application initialization
  151.         
  152.     NewLevelWindow();                // create the level window
  153.     InitLevelMeter();                // start up the level meter
  154.  
  155.     DoUpdateWindow(gLevelWindow);
  156.     
  157.     while (!gQuitFlag)                // handle events until quit
  158.         DoEvents();
  159.  
  160.     CleanUp();                        // clean up before quitting
  161. }
  162.  
  163. /*    ---------------------------------------------------------------------------------
  164.     Intialize
  165.     
  166.     First time application initialization
  167. */
  168.  
  169. #pragma segment Initialize
  170.  
  171. static void
  172. Initialize()
  173. {
  174.     InitGraf(&qd.thePort);
  175.     InitFonts();
  176.     InitWindows();
  177.     InitMenus();
  178.     TEInit();
  179.     InitDialogs(nil);
  180.     InitCursor();
  181.     
  182.     FlushEvents(everyEvent, 0);
  183.         
  184.     SetupMemory();
  185.     SetupEnvironment();
  186.     SetupMenus();
  187.     
  188.     gUseColour = ColourDepth() >= 4;
  189.     
  190. }
  191.  
  192. /*    ---------------------------------------------------------------------------------
  193.     SetupEnvironment
  194.     
  195.     This routine will determine the hardware and software operating environment
  196. */
  197.  
  198. #define RequestedVersion    1
  199.  
  200. static void
  201. SetupEnvironment()
  202. {
  203.     WNEIsImplemented = true;
  204. }
  205.  
  206. /*
  207.     --- SetupMemory ------------------------------------------------------------------------------------
  208.     This routine will allocate some master pointers, grow the heap,
  209.     and set up a grow zone function.
  210. */
  211. static void
  212. SetupMemory()
  213. {
  214.     MaxApplZone();
  215.     MoreMasters();
  216.     MoreMasters();
  217. }
  218.  
  219.  
  220. /*
  221.     --- SetupMenus ------------------------------------------------------------------------------------
  222.     This routine will set up the LeadSheet menus.
  223. */
  224.  
  225. static void
  226. SetupMenus()
  227. {
  228.     Handle menuBarHndl;
  229.     
  230.     // load up the apple and file menu
  231.     menuBarHndl = GetNewMBar(kMenuBarID);        // read menus into menu bar
  232.     SetMenuBar(menuBarHndl);                    // install menus
  233.     DisposeHandle(menuBarHndl);
  234.     
  235.     AppendResMenu(GetMenuHandle(kAppleMenu), 'DRVR');        // add DA names to Apple menu
  236.     
  237.     DrawMenuBar();
  238. }
  239.  
  240. #pragma segment Main
  241.  
  242. /*    ---------------------------------------------------------------------------------
  243.     DoEvents
  244.     
  245.     This is the LaunchPad Main Event Loop. We fetch events from the event queue, 
  246.     and call the proper handlers for the given event class. 
  247. */
  248.  
  249. #define kSleepTicks    0
  250.  
  251. static void
  252. DoEvents()
  253. {
  254.     Boolean newEvent;                // is there an event in the queue
  255.     EventRecord event;                // the event record    
  256.  
  257.     /*
  258.         Look for an event in the event queue.
  259.     */
  260.     if (WNEIsImplemented) {
  261.         newEvent = WaitNextEvent(everyEvent, &event, kSleepTicks, nil);
  262.     } else {
  263.         SystemTask();
  264.         newEvent = GetNextEvent(everyEvent, &event);
  265.     }
  266.         
  267.     DoIdle();            // idle processing
  268.     
  269.     /*    Dispatch on the type of event */    
  270.     switch(event.what) {
  271.         case mouseDown:
  272.             DoMouseDown(&event);
  273.             break;
  274.         case keyDown:
  275.         case autoKey:
  276.             DoKeyDown(&event);
  277.             break;
  278.         case updateEvt:
  279.             DoUpdateWindow((WindowPtr)event.message);
  280.             break;
  281.         case activateEvt:
  282.             DoActivateWindow(event.modifiers & activeFlag, (WindowPtr)event.message);
  283.             break;
  284.         case diskEvt:
  285.             break;
  286.         case osEvt:
  287.             /*
  288.                 Check for suspend or resume events (the only ones now defined)
  289.             */
  290.             if ((event.message >> 24) == suspendResumeMessage) {
  291.                 if (event.message & resumeFlag)
  292.                     DoResume(&event);
  293.                 else
  294.                     DoSuspend(&event);
  295.             }
  296.             break;
  297.         case highLevelEventMask:
  298.             break;
  299.     }
  300.     
  301.     DoIdle();            // more idle processing
  302. }
  303.  
  304. /*    --- DoMouseDown ----------------------------------------------------------------------------------
  305.     This routine handles mouseDown events.
  306. */
  307. static void
  308. DoMouseDown(event)
  309. EventRecord *event;
  310. {
  311.     short whichPart;
  312.     WindowPtr window;
  313.     Rect dragBounds;
  314.     long selection;
  315.     short theMenu, theItem;
  316.     
  317.     /*
  318.         Determine where the mouseDown event occurred
  319.     */
  320.     whichPart = FindWindow(event->where, &window);
  321.     
  322.     switch(whichPart) {
  323.         case inMenuBar:
  324.             selection = MenuSelect(event->where);
  325.             theMenu = HiWord(selection);
  326.             theItem = LoWord(selection);
  327.             DoMenuCommand(theMenu, theItem);
  328.             break;
  329.         case inSysWindow:
  330.             SystemClick(event, window);
  331.             break;
  332.         case inContent:
  333.             DoContentClick(event, window);
  334.             break;
  335.         case inDrag:
  336.             dragBounds = qd.screenBits.bounds;
  337.             DragWindow(window, event->where, &dragBounds);
  338.             break;
  339.         case inGrow:
  340.             break;
  341.         case inGoAway:
  342.             break;
  343.         case inZoomIn:
  344.         case inZoomOut:
  345.             break;
  346.         case inDesk:        // a click in the desktop will deselect the current selection
  347.             break;
  348.     }
  349. }
  350.  
  351.  
  352. /*    ---------------------------------------------------------------------------------
  353.     DoKeyDown
  354.     
  355.     This routine handles keyDown events.
  356. */
  357. static void
  358. DoKeyDown (EventRecord *event)
  359. {
  360. #pragma unused(event)
  361.     gQuitFlag = true;
  362. }
  363.  
  364. /*    ---------------------------------------------------------------------------------
  365.     DoUpdateWindow
  366.  
  367.     This routine handles update events for the given window
  368. */
  369.  
  370. static void
  371. DoUpdateWindow (window)
  372. WindowPtr window;
  373. {
  374.     GrafPtr savePort;
  375.     Rect boundsRect;
  376.     short i;
  377.     
  378.     GetPort(&savePort);
  379.     
  380.     BeginUpdate(window);        // set up the clipRgn and visRgn
  381.     
  382.     SetPort(window);
  383.     
  384.     RGBForeColor(&blackRGB);
  385.     EraseRect(&window->portRect);
  386.     boundsRect = window->portRect;
  387.     InsetRect(&boundsRect, 2, 2);
  388.     PaintRect(&boundsRect);
  389.     
  390.     // redraw the bar graph frames up to the current level
  391.     for (i = 1; i <= gLastLevel; i++) {
  392.         DrawOneBar(&gMeterRect, i, gUseColour);
  393.     }
  394.     
  395.     EndUpdate(window);            // restore window regions
  396.     
  397.     SetPort(savePort);
  398. }
  399.  
  400. /*    --- DoActivateWindow ----------------------------------------------------------------------------------
  401.     This routine handles activate events for the given window
  402. */
  403. static void
  404. DoActivateWindow (Boolean activate,WindowPtr window)
  405. {
  406. #pragma unused(activate, window)
  407. }
  408.  
  409. /*    ---------------------------------------------------------------------------------
  410.     DoMenuCommand
  411.     
  412.     This routine handles menu selection dispatching
  413. */
  414.  
  415. static void
  416. DoMenuCommand(short theMenu, short theItem)
  417. {
  418.     switch (theMenu) {
  419.         case kAppleMenu:
  420.             if (theItem == kAboutItem) {
  421.                 DoAbout();
  422.             } else {
  423.                 DoDeskAccessory(theMenu, theItem);
  424.             }
  425.             break;
  426.         case kFileMenu:
  427.             if (theItem == kQuitItem) {
  428.                 ExitToShell();
  429.             }
  430.             break;
  431.         default:
  432.             break;
  433.     }
  434.     
  435.     HiliteMenu(0);
  436. }
  437.  
  438. /*    --- DoContentClick ------------------------------------------------------------------------------
  439.     This routine handles clicks in the content area of a window. If the option key is down,
  440.     drag the launch window to another location on the screen.
  441. */
  442. static void
  443. DoContentClick(event, window)
  444. EventRecord *event;
  445. WindowPtr window;
  446. {
  447. #pragma unused(event)
  448.  
  449.     GrafPtr savePort;
  450.     Point clickPt;
  451.     
  452.     /*
  453.         If the clicked window isn't frontmost, simply select it
  454.     */
  455.     if (window != FrontWindow()) {
  456.         SelectWindow(window);
  457.         return;
  458.     }
  459.     
  460.     GetPort(&savePort);                    // save the current port
  461.     SetPort(window);                    // set the port to the clicked window
  462.         
  463.     clickPt = event->where;                // convert point to local coordindates
  464.     GlobalToLocal(&clickPt);
  465.     
  466.     DragWindow(window, event->where, &qd.screenBits.bounds);
  467.         
  468.     SetPort(savePort);                    // restore the saved port
  469. }
  470.  
  471. /*    ---------------------------------------------------------------------------------
  472.     DoSuspend
  473.  
  474.     This routine handles multifinder suspend events.
  475. */
  476. static void
  477. DoSuspend(event)
  478. EventRecord *event;
  479. {
  480. #pragma unused(event)
  481.     DoActivateWindow(false, gLevelWindow);
  482. }
  483.  
  484. /*    ---------------------------------------------------------------------------------
  485.     DoResume
  486.     
  487.     This routine handles multifinder resume events.
  488. */
  489. static void
  490. DoResume(event)
  491. EventRecord *event;
  492. {
  493. #pragma unused(event)
  494.     InitCursor();
  495.     DoActivateWindow(true, gLevelWindow);
  496. }
  497.  
  498. /*    ---------------------------------------------------------------------------------
  499.     DoDeskAccessory
  500.     
  501.     Handle desk accessory selections from the apple menu
  502. */
  503.  
  504. static void
  505. DoDeskAccessory(short whichMenu,short whichItem)
  506. {
  507.     Str255 daName;
  508.     
  509.     GetMenuItemText(GetMenuHandle(whichMenu), whichItem, daName);
  510.         
  511.     (void) OpenDeskAcc(daName);
  512. }
  513.  
  514. /*    ---------------------------------------------------------------------------------
  515.     NewLevelWindow
  516.  
  517.     This routine will create the sound level window.
  518. */
  519.  
  520. static void
  521. NewLevelWindow()
  522. {
  523.     Point **userOffsetPtHndl;
  524.     Rect screenRect;
  525.     Rect globalRect;
  526.     
  527.     if (ColourQDExists())
  528.         gLevelWindow = GetNewCWindow(128, nil, (WindowPtr)-1);
  529.     else
  530.         gLevelWindow = GetNewWindow(128, nil, (WindowPtr)-1);
  531.         
  532.     SetPort(gLevelWindow);
  533.     
  534.     // load the offset resource that tells us if the user has dragged
  535.     // the window somewhere else. make sure the window remains visible
  536.     // and eventually put this in a pref file where it belongs
  537.     
  538.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  539.     if (userOffsetPtHndl != nil) {
  540.         screenRect = qd.screenBits.bounds;
  541.         InsetRect(&screenRect, 5, 5);        // a little bit of a margin
  542.         
  543.         // the port is set for our window, so convert (0,0) to global for the
  544.         // global topLeft of the window
  545.         globalRect.left = (**userOffsetPtHndl).h;
  546.         globalRect.top = (**userOffsetPtHndl).v;
  547.         globalRect.right = globalRect.left + (gLevelWindow->portRect.right - gLevelWindow->portRect.left);
  548.         globalRect.bottom = globalRect.top + (gLevelWindow->portRect.bottom - gLevelWindow->portRect.top);
  549.  
  550.         if (SectRect(&screenRect, &globalRect, &globalRect)) {
  551.             MoveWindow(gLevelWindow, globalRect.left, globalRect.top, true);
  552.         } 
  553.         
  554.         ReleaseResource((Handle)userOffsetPtHndl);
  555.     }
  556.     
  557.     ShowWindow(gLevelWindow);            // make it visible
  558. }
  559.  
  560. /*    ---------------------------------------------------------------------------------
  561.     CleanUp
  562.  
  563.     This routine will clean up before exiting to the Finder
  564. */
  565. static void
  566. CleanUp()
  567. {
  568.     Point **userOffsetPtHndl;
  569.     Point globalPt;
  570.     
  571.     CloseLevelMeter();                // close up the level meter
  572.  
  573.     // save the position of the top/left corner of the window in case the user
  574.     // has moved it. For now, back to the WPos resource in the app will have to do
  575.     userOffsetPtHndl = (Point **)GetResource('WPos', 128);
  576.     if (userOffsetPtHndl != nil) {
  577.         SetPort(gLevelWindow);
  578.         globalPt.h = globalPt.v = 0;
  579.         LocalToGlobal(&globalPt);
  580.         
  581.         **userOffsetPtHndl = globalPt;
  582.         ChangedResource((Handle)userOffsetPtHndl);
  583.         WriteResource((Handle)userOffsetPtHndl);
  584.     }
  585.     if (gLevelWindow != nil)
  586.         DisposeWindow(gLevelWindow);
  587. }
  588.  
  589. /*    ---------------------------------------------------------------------------------
  590.     MakeCursor
  591.     
  592.     This routine will set the cursor to the 'CURS' resource whose id is given.
  593. */
  594. void
  595. MakeCursor(short cursorID)
  596. {
  597. #pragma unused(cursorID)
  598. }
  599.  
  600.  
  601. /*    ---------------------------------------------------------------------------------
  602.     DoIdle
  603.     
  604. */
  605. static void
  606. DoIdle()
  607. {
  608.     OSErr err;
  609.     short recordingStatus = 0;                        // status of recording session
  610.     short meterLevel = 0;                            // current meter level
  611.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  612.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  613.     unsigned long totalMsecsToRecord;
  614.     unsigned long numberOfMsecsRecorded;
  615.     GrafPtr savePort;
  616.     
  617.     GetPort(&savePort);
  618.     SetPort(gLevelWindow);
  619.     
  620.     /* get the sound input status    */
  621.     err = SPBGetRecordingStatus(gSoundInputRefNum,
  622.                                 &recordingStatus,
  623.                                 &meterLevel,
  624.                                 &totalSamplesToRecord,
  625.                                 &numberOfSamplesRecorded,
  626.                                 &totalMsecsToRecord,
  627.                                 &numberOfMsecsRecorded);
  628.              
  629.     DrawTheMeter(&gMeterRect, 
  630.                    gNumElements, 
  631.                    &gPeakLevel, 
  632.                    &gLastLevel, 
  633.                    &gPeakTimeOut, 
  634.                    meterLevel,
  635.                    gRedZone,
  636.                    &blueRGB,
  637.                    &redRGB,
  638.                    &blackRGB,
  639.                    gUseColour);
  640.                    
  641.     SetPort(savePort);
  642. }
  643.  
  644. /*    ---------------------------------------------------------------------------------
  645.     OptionTest
  646.     
  647.     Returns whether the option key is being pressed
  648. */
  649. static Boolean
  650. OptionTest()
  651. {
  652.     KeyMap theKeys;
  653.     
  654.     GetKeys(theKeys);
  655.     
  656.     if (theKeys[1] & 4)
  657.         return(true);
  658.     else
  659.         return(false);
  660.  
  661. }
  662.  
  663. /*    ---------------------------------------------------------------------------------
  664.     CmdTest
  665.     
  666.     Returns whether the command (apple) key is being pressed
  667. */
  668. static Boolean
  669. CmdTest()
  670. {
  671.     KeyMap theKeys;
  672.     
  673.     GetKeys(theKeys);
  674.     
  675.     if (theKeys[1] & 0x8000)
  676.         return(true);
  677.     else
  678.         return(false);
  679.  
  680. }
  681.  
  682. /*    ---------------------------------------------------------------------------------
  683.     ShiftTest
  684.     
  685.     Returns whether the shift key is being pressed
  686. */
  687. static Boolean
  688. ShiftTest()
  689. {
  690.     KeyMap theKeys;
  691.     
  692.     GetKeys(theKeys);
  693.     
  694.     if (theKeys[1] & 1)
  695.         return(true);
  696.     else
  697.         return(false);
  698.  
  699. }
  700.  
  701. #define ROM85            (*(short *)0x28e)
  702. #define TWOHIGHMASK        0xc000
  703. #define COLOURQDEXISTS    !(ROM85 & TWOHIGHMASK)
  704.  
  705.  
  706. /*    ---------------------------------------------------------------------------------
  707.     ColourQDExists
  708.     
  709.     This routine is called to determine whether or not color quickdraw
  710.     exists in the current roms. It checks the lom memory global just
  711.     like Apple usually does…
  712. */
  713. Boolean
  714. ColourQDExists()
  715. {
  716.     return(COLOURQDEXISTS);
  717. }
  718.  
  719. /*    ---------------------------------------------------------------------------------
  720.     ColourQDIsOn
  721.     
  722.     This routine is called to determine if colour quickdraw exists
  723.     in the current roms and if the main screen is displaying color.
  724. */
  725.  
  726. #define QD_MIN_FOR_COLOUR    4
  727.  
  728. Boolean
  729. ColourQDIsOn()
  730. {
  731.     GDHandle maindevice;
  732.     PixMapHandle mainpix;
  733.     short depth;
  734.  
  735.     /*
  736.         First confirm that color is available in the roms
  737.     */
  738.     if (COLOURQDEXISTS) {
  739.         /*
  740.             Determine if the main device is displaying color
  741.         */
  742.         maindevice = GetMainDevice();
  743.         mainpix = (*maindevice)->gdPMap;
  744.         
  745.         /*
  746.             Check the depth of the pixel map
  747.         */
  748.         depth = (*mainpix)->pixelSize;
  749.         
  750.         if (depth < QD_MIN_FOR_COLOUR)
  751.             return(false);
  752.         else
  753.             return(true);
  754.     } else
  755.         return(false);
  756. }
  757.  
  758. short
  759. ColourDepth()
  760. {
  761.     GDHandle maindevice;
  762.     PixMapHandle mainpix;
  763.  
  764.     /*
  765.         First confirm that color is available in the roms
  766.     */
  767.     if (COLOURQDEXISTS) {
  768.         /*
  769.             Determine if the main device is displaying color
  770.         */
  771.         maindevice = GetMainDevice();
  772.         mainpix = (*maindevice)->gdPMap;
  773.         
  774.         /*
  775.             Check the depth of the pixel map
  776.         */
  777.         return (*mainpix)->pixelSize;
  778.     } else
  779.         return 1;
  780. }
  781.  
  782. /*    ---------------------------------------------------------------------------------
  783.     TwitchToFinder
  784.     
  785.     Switch back to Finder. Use the System 7.0 process manager if present
  786. */
  787. static void
  788. TwitchToFinder()
  789. {
  790.  
  791. }
  792.  
  793. /*    ---------------------------------------------------------------------------------
  794.     DrawTheMeter
  795.     
  796.     This routine is called to draw the volume meter during sound input.
  797. */
  798.  
  799. void
  800. DrawTheMeter(Rect *meterRect, short numElements, short *peakLevel, short *lastLevel, long *peakTimeout, short meterLevel, short redZone, RGBColor *blueRGB, RGBColor *redRGB, RGBColor *blackRGB, Boolean useColour)
  801. {
  802.     Rect barRect;
  803.     short i;
  804.     
  805.     PenNormal();
  806.     
  807.     // quantize the meterLevel based on number of elements
  808.     // in our bar chart
  809.     if (meterLevel > kMaxVolume)
  810.         meterLevel = kMaxVolume;
  811.         
  812.     meterLevel = (meterLevel * numElements) / kMaxVolume;
  813.     
  814.     // determine if this is a new peak value and erase the
  815.     // peak bar if it has timed out
  816.     if (meterLevel >= *peakLevel) {
  817.         *peakLevel = meterLevel;
  818.         *peakTimeout = TickCount() + kPeakTimeoutTicks; 
  819.     } else {
  820.         // has the current peak timed out?
  821.         if (*peakLevel > 0 && *peakTimeout <= TickCount()) {
  822.             if (*peakLevel >= *lastLevel) {
  823.             
  824.                 if (useColour)
  825.                     RGBForeColor(blackRGB);
  826.  
  827.                 GetBarRect(meterRect, *peakLevel, &barRect);
  828.                 PaintRect(&barRect);
  829.                 
  830.                 // erase the previous element (peak is two elements wide)
  831.                 if (*peakLevel > 1) {            // don’t erase a non-element
  832.                     GetBarRect(meterRect, *peakLevel - 1, &barRect);
  833.                     PaintRect(&barRect);
  834.                 }
  835.             }
  836.             *peakLevel = 0;
  837.         }
  838.     }
  839.         
  840.     // check whether the signal is now stronger than last time
  841.     if (meterLevel > *lastLevel) {
  842.         for (i = *lastLevel + 1; i <= meterLevel; i++) {
  843.             GetBarRect(meterRect, i, &barRect);                    // get the rect for this bar
  844.             
  845.             if (useColour) {                                    // watch out for colour
  846.                 if (i >= redZone)                                // are we beyond the clipping point?
  847.                     RGBForeColor(redRGB);                        // draw in red to show distortion
  848.                 else
  849.                     RGBForeColor(blueRGB);                        // draw in blue for normal signal
  850.                 PaintRect(&barRect);                            // fill the element in
  851.                 
  852.             } else
  853.                 EraseRect(&barRect);                            // no colour so white will have to do
  854.         }
  855.         *lastLevel = meterLevel;                                // remember for next time
  856.     }
  857.         
  858.     // check whether the signal level is now weaker than last time
  859.     // and if it is remove the rightmost two level bar currently on
  860.     if (meterLevel < *lastLevel) {
  861.         if (*peakLevel != 0) {
  862.             if (*lastLevel == *peakLevel)                // don’t erase the peak!
  863.                 *lastLevel -= 2;
  864.             if (*lastLevel == *peakLevel -1)
  865.                 *lastLevel -= 1;
  866.         }
  867.         
  868.         if (useColour)
  869.             RGBForeColor(blackRGB);
  870.  
  871.         for (i = 0; i < 2; i++) {
  872.             if (*lastLevel > 0) {
  873.                 GetBarRect(meterRect, *lastLevel, &barRect);
  874.                 PaintRect(&barRect);
  875.                 *lastLevel -= 1;
  876.             }
  877.             
  878.             if (*lastLevel < 0 )
  879.                 *lastLevel = 0;
  880.         }
  881.         
  882.     }
  883.  
  884.     if (useColour)
  885.         RGBForeColor(blackRGB);
  886.  
  887. }
  888.  
  889. /*    ---------------------------------------------------------------------------------
  890.     GetBarRect
  891. */
  892. void
  893. GetBarRect(Rect *meterRect, short whichBar, Rect *barRect)
  894. {
  895.     if (whichBar == 0) {
  896.         return;
  897.     }
  898.     
  899.     SetRect(barRect, meterRect->left + 2, 
  900.                      meterRect->top + 1, 
  901.                      meterRect->left + 4, 
  902.                      meterRect->bottom - 1);
  903.                      
  904.     OffsetRect(barRect, (whichBar - 1)* 3, 0);
  905. }
  906.  
  907. /*    -------------------------------------------------------------------------------
  908.     InitLevelMeter
  909. */
  910.  
  911. void
  912. InitLevelMeter()
  913. {
  914.     RGBColor **colorRsrcHndl;
  915.     OSErr err;
  916.     
  917.     // load the colours used for the bar chart
  918.     colorRsrcHndl = (RGBColor **)GetResource(128, 'RGBv');
  919.     if (colorRsrcHndl) {
  920.         blueRGB = **colorRsrcHndl;
  921.         ReleaseResource((Handle)colorRsrcHndl);
  922.     } else {
  923.         blueRGB.red = 39321;        // flourescant blue
  924.         blueRGB.green = 65535;
  925.         blueRGB.blue = 65535;
  926.     }
  927.     
  928.     colorRsrcHndl = (RGBColor **)GetResource(129, 'RGBv');
  929.     if (colorRsrcHndl) {
  930.         redRGB = **colorRsrcHndl;
  931.         ReleaseResource((Handle)colorRsrcHndl);
  932.     } else {
  933.         redRGB.red = 65535;            // peak level red
  934.         redRGB.green = 0;
  935.         redRGB.blue = 0;
  936.     }
  937.     
  938.     blackRGB.red = 0;
  939.     blackRGB.green = 0;
  940.     blackRGB.blue = 0;
  941.     
  942.     gMeterRect = gLevelWindow->portRect;
  943.     InsetRect(&gMeterRect, 2, 2);
  944.     
  945.     gNumElements = (gMeterRect.right - gMeterRect.left - 2) / 3;
  946.     if (gNumElements > 255)
  947.         gNumElements = 255;
  948.  
  949.     gPeakTimeOut = 0;
  950.     gPeakLevel = 0;
  951.     gLastLevel = 0;
  952.     gRedZone = (gNumElements * 2) / 3;
  953.  
  954.     // open the sound input device
  955.     err = OpenTheSoundDevice();
  956. }
  957.  
  958.  
  959. // watch out for other usages of the soundInputRefNum
  960.  
  961. static void
  962. CloseLevelMeter()
  963. {
  964.     OSErr err;
  965.     
  966.     err = SPBCloseDevice(gSoundInputRefNum);
  967.     gSoundInputRefNum = 0;
  968. }
  969.  
  970. #define kAboutDLOG            128
  971. #define kAboutMeterItem        4
  972.  
  973. short    gAboutPeakLevel;
  974. short    gAboutLastLevel;
  975. long    gAboutPeakTimeOut;
  976.  
  977. void
  978. DoAbout()
  979. {
  980.     DialogPtr dialog;
  981.     short itemHit;
  982.     GrafPtr savePort;
  983.     ModalFilterUPP aboutFilterProcUPP;
  984.     
  985.     /* create a UPP for our modal dialog filter */
  986.     aboutFilterProcUPP = NewModalFilterProc(AboutFilterProc);
  987.     
  988.     GetPort(&savePort);
  989.     dialog = GetNewDialog(kAboutDLOG, nil, (WindowPtr)-1);
  990.     SetPort(dialog);
  991.     
  992.     // ••• force an update event first
  993.     UpdateAboutDialog((WindowPtr)dialog);
  994.     
  995.     gAboutPeakLevel = 0;
  996.     gAboutLastLevel = 0;
  997.     gAboutPeakTimeOut = 0;
  998.     
  999.     do {
  1000.         ModalDialog(aboutFilterProcUPP, &itemHit);
  1001.     } while (itemHit != ok);
  1002.     
  1003.     SetPort(savePort);
  1004.     DisposeDialog(dialog);
  1005.     
  1006.     DisposeRoutineDescriptor(aboutFilterProcUPP);
  1007.  
  1008. }
  1009.  
  1010. OSErr OpenTheSoundDevice()
  1011. {
  1012.     OSErr err;
  1013.     short meterState;
  1014.     
  1015.     // set the default zone to the system heap so that the SI manager will
  1016.     // possibly get fooled into allocating there instead of my heap
  1017.     
  1018.     SetZone(SystemZone());
  1019.     err = SPBOpenDevice(nil, siWritePermission, &gSoundInputRefNum);
  1020.     if (err != noErr) 
  1021.         goto BailOut;
  1022.         
  1023.     // turn on sound metering
  1024.     meterState = 1;                // turn it on
  1025.     err = SPBSetDeviceInfo(gSoundInputRefNum, siLevelMeterOnOff, (char *)&meterState);
  1026.  
  1027. BailOut:
  1028.     SetZone(ApplicationZone());
  1029.     
  1030.     return err;
  1031. }
  1032.  
  1033. void
  1034. DrawOneBar(Rect *meterRect, short whichBar, Boolean useColour)
  1035. {
  1036.     Rect barRect;
  1037.     
  1038.     GetBarRect(meterRect, whichBar, &barRect);            // get the rect for this bar
  1039.     
  1040.     if (useColour) {                                    // watch out for colour
  1041.         if (whichBar >= gRedZone)                        // are we beyond the clipping point?
  1042.             RGBForeColor(&redRGB);                        // draw in red to show distortion
  1043.         else
  1044.             RGBForeColor(&blueRGB);                        // draw in blue for normal signal
  1045.         PaintRect(&barRect);                            // fill the element in
  1046.         
  1047.     } else
  1048.         EraseRect(&barRect);                            // no colour so white will have to do
  1049. }
  1050.  
  1051. /*    ---------------------------------------------------------------------------------
  1052.     AboutFilterProc
  1053.     
  1054. */
  1055.  
  1056. #define CRKey            0x0d
  1057. #define EnterKey         0x03
  1058. #define EscapeKey        0x1b
  1059.  
  1060. pascal Boolean
  1061. AboutFilterProc(DialogPtr dialog, EventRecord *event, short *item)
  1062. {
  1063.     unsigned char theKey;
  1064.     WindowPtr window;
  1065.     short itemType;
  1066.     Handle itemHndl;
  1067.     Rect itemRect;
  1068.     OSErr err;
  1069.     short recordingStatus = 0;                        // status of recording session
  1070.     short meterLevel = 0;                            // current meter level
  1071.     unsigned long totalSamplesToRecord = 0;            // total number of samples
  1072.     unsigned long numberOfSamplesRecorded = 0;        // number of samples recorded
  1073.     unsigned long totalMsecsToRecord;
  1074.     unsigned long numberOfMsecsRecorded;
  1075.         
  1076.     /* get the sound input status    */
  1077.     if (event->what != updateEvt) {
  1078.         err = SPBGetRecordingStatus(gSoundInputRefNum,
  1079.                                     &recordingStatus,
  1080.                                     &meterLevel,
  1081.                                     &totalSamplesToRecord,
  1082.                                     &numberOfSamplesRecorded,
  1083.                                     &totalMsecsToRecord,
  1084.                                     &numberOfMsecsRecorded);
  1085.                  
  1086.         GetDialogItem(dialog, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1087.         InsetRect(&itemRect, 2, 2);
  1088.         
  1089.         SetPort(dialog);
  1090.         
  1091.         DrawTheMeter(&itemRect, 
  1092.                        gNumElements, 
  1093.                        &gAboutPeakLevel, 
  1094.                        &gAboutLastLevel, 
  1095.                        &gAboutPeakTimeOut, 
  1096.                        meterLevel,
  1097.                        gRedZone,
  1098.                        &blueRGB,
  1099.                        &redRGB,
  1100.                        &blackRGB,
  1101.                        gUseColour);
  1102.                        
  1103.         // check for nearly maximum volume to dismiss dialog;
  1104.         if (meterLevel > 250) {
  1105.             HiliteItem(dialog, ok);
  1106.             *item = ok;
  1107.             return true;
  1108.         }
  1109.         
  1110.     }
  1111.     
  1112.     switch(event->what) {
  1113.         case mouseDown:
  1114.             break;
  1115.         case keyDown:
  1116.         case autoKey:
  1117.             theKey = event->message & charCodeMask;
  1118.             
  1119.             if (theKey == CRKey || theKey == EnterKey || theKey == EscapeKey) {
  1120.                 HiliteItem(dialog, ok);
  1121.                 *item = ok;
  1122.                 return true;
  1123.             }
  1124.                         
  1125.             break;
  1126.         case updateEvt:
  1127.             window = (WindowPtr)event->message;
  1128.             if (window == (WindowPtr)dialog) {
  1129.                 UpdateAboutDialog(window);
  1130.             }
  1131.             break;
  1132.         default:
  1133.             break;
  1134.     }
  1135.     return false;
  1136. }
  1137.  
  1138. void
  1139. UpdateAboutDialog(WindowPtr window)
  1140. {
  1141.     GrafPtr savePort;
  1142.     short itemType;
  1143.     Handle itemHndl;
  1144.     Rect itemRect;
  1145.     
  1146.     BeginUpdate(window);
  1147.     GetPort(&savePort);
  1148.     SetPort(window);
  1149.     
  1150.     UpdateDialog((DialogPtr)window, window->visRgn);
  1151.     
  1152.     PenNormal();
  1153.     GetDialogItem((DialogPtr)window, kAboutMeterItem, &itemType, &itemHndl, &itemRect);
  1154.     FrameRect(&itemRect);
  1155.     InsetRect(&itemRect, 2, 2);
  1156.     PaintRect(&itemRect);
  1157.     
  1158.     SetPort(savePort);
  1159.     EndUpdate(window);
  1160. }
  1161.  
  1162. /*    ---------------------------------------------------------------------------------
  1163.     HiliteItem
  1164.     
  1165.     This routine will hilite the given item control so the user knows
  1166.     which item has been activated by the  key equivalent
  1167. */
  1168.  
  1169. #define ButtonInvertState    10        // inverted state for push buttons
  1170. #define CheckRadioState        11        // inverted state for checks and radios
  1171. #define NormalState            0        // non-inverted state
  1172. #define SleepDuration        10        // pause for 10 ticks
  1173.  
  1174. void
  1175. HiliteItem(DialogPtr dialog, short theitem)
  1176. {
  1177.     Handle thehndl;
  1178.     short thetype;
  1179.     Rect thebox;
  1180.     unsigned long junkTicks;
  1181.     short hiliteState;
  1182.     
  1183.     /*
  1184.         Get Handle to the item
  1185.     */
  1186.     GetDialogItem(dialog, theitem, &thetype, &thehndl, &thebox);
  1187.     
  1188.     switch (thetype) {
  1189.         case btnCtrl + ctrlItem:
  1190.             hiliteState = ButtonInvertState;
  1191.             break;
  1192.         case chkCtrl + ctrlItem:
  1193.         case radCtrl + ctrlItem:
  1194.             hiliteState = CheckRadioState;
  1195.             break;
  1196.         default:
  1197.             return;
  1198.             break;
  1199.     }
  1200.     /*
  1201.         Invert the control for a little while
  1202.     */
  1203.     HiliteControl((ControlHandle)thehndl, hiliteState);
  1204.     Delay(SleepDuration, &junkTicks);
  1205.     HiliteControl((ControlHandle)thehndl, NormalState);
  1206. }
  1207.